/**
 * 
 */
package com.roshan.web.security.service;

import java.util.Date;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.roshan.web.security.context.RoarSecurityContext;
import com.roshan.web.security.cryptography.IRoarCryptographyProvider;
import com.roshan.web.security.domain.RoarAuthenticationToken;
import com.roshan.web.security.domain.RoarMFAAuthenticationToken;
import com.roshan.web.security.util.CommonUtils;
import com.roshan.web.security.util.CookieUtils;
import com.roshan.web.security.util.constants.SecurityConstants;
import com.roshan.web.util.SecurityUtils;
import com.roshan.web.util.constants.CommonConstants;

@Service("cookieService")
public class CookieService implements ICookieService {

    @Autowired
    private IRoarCryptographyProvider cryptographyProvider;

    private @Value("${roar.security.cookies.is.secure}")
    Boolean isCookieSecure;

    private @Value("${roar.security.cookie.mfa.name}")
    String mfaCookieName;

    private @Value("${roar.security.cookies.is.httponly}")
    boolean isHttpOnly;

    private @Value("${roar.security.cookie.mfa.max.age}")
    int mfaMaxAge;

    private @Value("${roar.security.cookie.name}")
    String securityCookieName;

    private @Value("${roar.security.cookie.max.age}")
    int maxAge;

    private @Value("${roar.security.cookies.timeout.name}")
    String timeoutCookieName;

    private void addOrUpdateCookie(Object object, String cookieName, int maxAgeVal, HttpServletRequest request,
            HttpServletResponse response) {

        if (StringUtils.hasText(cookieName)) {

            String strObject = CommonUtils.toXmlString(object);
            if (StringUtils.hasText(strObject)) {
                addOrUpdateCookieContent(cookieName, maxAgeVal, request, response, strObject, null);
            }
        }
    }

    @Override
    public Object retrieveCookiePersistedObject(String key, HttpServletRequest request) {

        String decryptedCookieValue = retrieveDecryptedCookieValue(key, request);
        if (!StringUtils.hasText(decryptedCookieValue)) {
            return null;
        }
        return CommonUtils.toObject(decryptedCookieValue);

    }

    @Override
    public void removeCookie(String cookieName, HttpServletRequest request, HttpServletResponse response) {

        Cookie cookie = CookieUtils.getCookie(cookieName, request);

        if (cookie != null) {
            cookie.setMaxAge(0);
            cookie.setPath(CommonConstants.SLASH);
            cookie.setValue(SecurityConstants.REMOVE_COOKIE_VALUE);
            addToResponse(response, cookie);
        }

    }

    @Override
    public Cookie getCookie(String cookieName, HttpServletRequest request) {
        if (request.getCookies() != null && request.getCookies().length > 0) {
            for (Cookie cookie : request.getCookies()) {
                if (cookie.getName().equalsIgnoreCase(cookieName.trim()) && cookie.getMaxAge() > 0) {
                    return cookie;
                }
            }
        }
        return null;
    }

    @Override
    public Cookie retrieveCookie(String key, HttpServletRequest request) {
        if (key == null || request == null) {
            return null;
        }
        return CookieUtils.getCookie(key, request);
    }

    @Override
    public String retrieveDecryptedCookieValue(String key, HttpServletRequest request) {
        if (key == null || request == null) {
            return null;
        }
        Cookie cookie = CookieUtils.getCookie(key, request);
        if (cookie == null) {
            return null;
        }
        String encryptedCookieValue = cookie.getValue();
        if (!StringUtils.hasText(encryptedCookieValue)) {
            return null;
        }

        return this.cryptographyProvider.decrypt(encryptedCookieValue);
    }

    private void addOrUpdateCookieContent(String cookieName, int maxAgeVal, HttpServletRequest request,
            HttpServletResponse response, String cookieContent, String cookiePath) {

        String whiteSpacesFreeCookieContent = SecurityUtils.removeCarriageReturnAndLineFeed(cookieContent);

        String strEncoded = this.cryptographyProvider.encrypt(whiteSpacesFreeCookieContent);
        Cookie cookie = CookieUtils.getCookie(cookieName, request);
        if (cookie != null) {
            cookie.setValue(strEncoded);
        } else {
            cookie = new Cookie(cookieName, strEncoded);
        }
        cookie.setMaxAge(maxAgeVal);
        cookie.setPath(!StringUtils.hasText(cookiePath) ? CommonConstants.SLASH : cookiePath);
        cookie.setHttpOnly(this.isHttpOnly);
        addToResponse(response, cookie);
    }

    private void addToResponse(HttpServletResponse response, Cookie cookie) {

        if (this.isCookieSecure != null && this.isCookieSecure.booleanValue()) {
            cookie.setSecure(this.isCookieSecure.booleanValue());
        }
        response.addCookie(cookie);

    }

    @Override
    public boolean isValidMFACookieContent(String mfaCookieContent, String userName, String userUri) {
        return mfaCookieContent != null
                && mfaCookieContent.split(SecurityConstants.MFA_COOKIE_DELIMITER).length == SecurityConstants.MFA_COOKIE_CONTENT_ITEM_SIZE
                && mfaCookieContent.length() > userName.length()
                && (userName.equalsIgnoreCase(mfaCookieContent.substring(0, userName.length())) || userUri
                        .equals(mfaCookieContent.substring(mfaCookieContent.length() - userUri.length(),
                                mfaCookieContent.length())));

    }

    @Override
    public void createMFASecurityContext(String username, String customerUri, HttpServletRequest request,
            HttpServletResponse response) {

        String cookieContent = CookieUtils.getMFACookieContent(username, customerUri);
        String cookieName = CookieUtils.getMFACookieName(this.mfaCookieName, customerUri);

        addOrUpdateCookie(cookieContent, cookieName, this.mfaMaxAge, request, response);
    }

    @Override
    public void createRoarSecurityContext(RoarAuthenticationToken token, HttpServletRequest request,
            HttpServletResponse response) {

        RoarSecurityContext roarSecurityContext = new RoarSecurityContext();
        roarSecurityContext.setAuthentication(token);

        addOrUpdateCookie(roarSecurityContext, this.securityCookieName, this.maxAge, request, response);

    }

    @Override
    public void createRoarMFASecurityContext(RoarMFAAuthenticationToken mfaToken, HttpServletRequest request,
            HttpServletResponse response) {
        addOrUpdateCookie(mfaToken, this.mfaCookieName, this.mfaMaxAge, request, response);
    }

    @Override
    public void createRoarSecurityTimeOutContext(HttpServletRequest request, HttpServletResponse response) {

        long timeStamp = new Date().getTime();
        addOrUpdateCookie(Long.valueOf(timeStamp), this.timeoutCookieName, this.maxAge, request, response);

    }
}
